home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 2 / CU Amiga Magazine's Super CD-ROM 02 (1996)(EMAP Images)(GB)[!][issue 1996-04].iso / magazine / amiga_e / modulesmc1 / estuff.doc next >
Text File  |  1994-11-21  |  13KB  |  283 lines

  1.  
  2. These comments and ideas are based on the premise that faster is better.
  3. It's reasonable to assume that if well thought out assembly language is
  4. used to replace parts of a higher level language, then the program will run
  5. faster. It will also usually be smaller, which is no bad thing. E language
  6. makes incorporating assembly language extremely easy.
  7.  
  8. For max speed, the first thing is to have a good algorithm, i.e. write the
  9. program in E to make it run as fast as possible, before even thinking about
  10. assembly language. The best assembly language program may well be a dog if
  11. the algorithm is lousy to start with.
  12.  
  13. Use registers as much as possible. The simple minded approach is to just
  14. put OPT REG=5 at the top of the program. This gives EC the option to use its
  15. own judgment about which registers to use for what. Without this option
  16. variables get stored at offsets from registers A4 or A5, which means that
  17. access to the variables is slower than if the variables were stored in one of
  18. the data registers, of which EC uses D3 through D7. Time your program both
  19. with and without OPT REG=5, and note the difference in speed and size. Try it
  20. again with REG=4 or REG=3, which in some cases may be slightly faster. If
  21. there are more variables than you specify by the REG options, EC does a good
  22. job of deciding which variables should go into registers, but it may be that
  23. you can make a more educated choice of variables than EC can. If you know
  24. that a certain variable should be in a register, than use DEF var:REG to
  25. force it there. You can do this with no more than 5 variables, of course.
  26.  
  27. It doesn't make much sense to translate an entire program into assembly
  28. language if most of the running time of the program is spent churning around
  29. and around in one small part of the program. If you aren't sure what part of
  30. your program takes up most of the time, profile it. The E distribution
  31. provides a great profiler, AProf, in the Bin directory. Documentation for
  32. AProf is in Tools/AProf. To use it, the executable file to be profiled should
  33. have a symbol table, which you get by using the EC option "sym". To make it
  34. easy, use the alias capability of AmigaDOS. Type in "alias ecs ec sym", or
  35. better yet put it in your startup file.
  36.  
  37. Once you know what part of the program you are going to concentrate on, the
  38. easiest way to get started is to see how that part of the program is already
  39. coded by EC, and try to figure out more economical ways to code it. To do
  40. this, you need to disassemble an executable file. There are many debuggers
  41. and disassemblers in the public domain. My first recommendation is ADIS,
  42. by Martin Apel.    email: apel@physik.uni-kl.de
  43. I know that it is in the public domain, but have completely forgotten where
  44. I got it. It's well worth looking for. In many cases it will produce files
  45. which are essentially ready for reassembly. To use it with E the -ml option
  46. is necessary. Again an alias is appropriate, "alias adis adis -ml" I use
  47. "alias adis adis -ml -c2" because I have an Amiga 1200 which uses the 68020
  48. chip. Actually the -c2 option is necessary only when you are disassembling
  49. programs which have instructions contained in the 68020 but not the 68000
  50. chip. Not many programmers use them, which makes sense to ensure backward
  51. compatibility. The only ones I have ever used are some of the 32 bit divide
  52. and multiply instructions.
  53.  
  54. Disassembling isn't much use if you then can't find the code you are planning
  55. to improve. No problem: Surround the code with NOPs. NOP is 68000 for do
  56. nothing, and it can go anywhere in the program. Put the disassembled program
  57. into your text editor and look for NOP (or possibly nop if that's what your
  58. disassembler puts out). I use Matt Dillon's DME, which can find a NOP almost
  59. instantaneously.
  60. Example:
  61.  
  62. PROC   main()
  63. DEF i:REG
  64. NOP
  65. i++
  66. NOP
  67. ENDPROC
  68.  
  69. results in a bunch of stuff including:
  70.  
  71. main            LINK       A5,#$0
  72.             MOVEM.L       D7,-(SP)
  73.             NOP
  74.             ADDQ.L       #$1,D7
  75.             NOP
  76.             MOVEQ       #$0,D0
  77.             MOVEM.L       (SP)+,D7
  78.             UNLK       A5
  79.             RTS
  80.  
  81. The listing above is exactly as it came out of ADIS.
  82. Note: Most disassemblers put out SP instead of A7, but EC insists on A7.
  83. The listing shows that variable i is stored in D7, and that ADDQ.L #$1,D7 is
  84. equivalent to i++. Now try it again without the :REG.
  85.  
  86. main            LINK       A5,#-$4
  87.             NOP
  88.             ADDQ.L       #$1,-$4(A5)
  89.             NOP
  90.             MOVEQ       #$0,D0
  91.             UNLK       A5
  92.             RTS
  93.  
  94. Now i is stored at a memory location 4 below whatever address A5 is pointing
  95. at, and ADDQ.L #$1,-4(A5) is considerably slower than ADDQ.L #$1,D7. Note
  96. that the LINK instrucion makes available 4 bytes on the stack to stow i in.
  97. UNLK releases that stack space. Here's a more complicated example:
  98.  
  99. PROC   main()
  100. DEF i, j=0
  101. NOP
  102. FOR i:=1 TO 100 DO j++
  103. NOP
  104. ENDPROC
  105.  
  106. Which translates into:
  107.  
  108. main            LINK       A5,#-$8
  109.             MOVEQ       #$0,D0
  110.             MOVE.L       D0,-$8(A5)
  111.             NOP
  112.             MOVEQ       #$1,D0
  113.             MOVE.L       D0,-$4(A5)
  114. L$1B4            MOVEQ       #$64,D0
  115.             CMP.L       -$4(A5),D0
  116.             BMI.L       L$1CA
  117.             ADDQ.L       #$1,-$8(A5)
  118.             ADDQ.L       #$1,-$4(A5)
  119.             BRA.L       L$1B4
  120.  
  121. L$1CA            NOP
  122.             MOVEQ       #$0,D0
  123.             UNLK       A5
  124.             RTS
  125.  
  126. If DEF i,j=0 is changed to DEF i:REG, j=0:REG  the result is:
  127.  
  128. main            LINK       A5,#$0
  129.             MOVEM.L       D6-D7,-(SP)
  130.             MOVEQ       #$0,D0
  131.             MOVE.L       D0,D6
  132.             NOP
  133.             MOVEQ       #$1,D0   /* set up loop  */
  134.             MOVE.L       D0,D7
  135. L$1B4            MOVEQ       #$64,D0
  136.             CMP.L       D7,D0
  137.             BMI.L       L$1C4    /* get out of loop    */
  138.             ADDQ.L       #$1,D6   /* this is j  */
  139.             ADDQ.L       #$1,D7   /* increment loop counter  */
  140.             BRA.L       L$1B4    /* go to top of loop  */
  141.  
  142. L$1C4            NOP
  143.             MOVEQ       #$0,D0
  144.             MOVEM.L       (SP)+,D6-D7
  145.             UNLK       A5
  146.             RTS
  147.  
  148. This doesn't look much simpler than the previous one but runs a lot faster.
  149. So far we haven't written any assembly language. Let's improve on the line
  150. FOR i:=1 TO 100 DO j++
  151. and assume that DEF i:REG, j:REG is in the program.
  152.  
  153.   MOVEQ #99,i      /* these 3 instructions replace 8 above  */
  154. loop: ADDQ.L #1,j
  155.       DBRA i,loop
  156.  
  157. That's all there is to it. First 99 gets moved into some data register i.
  158. Let EC worry about which one it is. It's correct to put in 99 rather than 100
  159. because DBRA always takes one more trip through the loop than the starting
  160. value of i. The loop is traversed 100 times, each time adding 1 to j, again
  161. letting EC worry about what data register holds j.
  162.  
  163. For the real speed fanatics, sometimes it is practical to put the code for a
  164. function in line, even if it is code provided by a module or even by EC
  165. itself. The StrLen function is a good example. Note that this is StrLen,
  166. not EstrLen, and operates on any null-terminated string. The advantage of
  167. putting functions in line is that the overhead of pushing variables,
  168. branching, and returning takes a lot of time, and that time can be saved.
  169.  
  170.   MOVEQ #-1,D0
  171.   MOVEA.L str,A0
  172. loop:  TST.B (A0)+
  173.   DBEQ D0,loop
  174.   NOT.L D0
  175.  
  176. Here str is the name of the string whose length you want. D0 will hold that
  177. length when the instructions are completed. Note that DBEQ exits the loop
  178. when the zero condition flag is set, at which time D0 is some negative number.
  179. It would seem reasonable to use NEG on D0, since NEG does a two's complement
  180. negation, but NOT gives the correct answer. Putting this code in line does
  181. not increase the size of the program.
  182.  
  183. Another extremely small function is strcopy, assuming here that str and
  184. newstr are either Estrings or strings, i.e ARRAY OF CHAR.
  185.  
  186.   MOVEA.L str,A0
  187.   MOVEA.L newstr,A1
  188.   loop: MOVE.B (A0)+,(A1)+
  189.   BNE.S loop
  190.  
  191. For the ultimate in speed, where you want a very small string constant to be
  192. shoved into a string, try some variation on this:
  193.  
  194.   MOVEA.L str,A0
  195.   MOVE.L #"abc\0",(A0)
  196.  
  197. It is not my intention to attempt to teach assembly language, just to get
  198. someone who knows a little assembly language started on incorporating it into
  199. E. Beginners will find it easier to become proficient this way than by trying
  200. to write a complete assembly lanuage from scratch. The two books which I have
  201. found most useful in getting started with assembly language are Programming
  202. the 68000 by Steve Williams, (SYBEX) and Amiga Machine Language by Stefan
  203. Dittrich (Abacus). A probably unnecessary word of caution: If you program in
  204. assembly language, the Guru is coming! You might minimize its impact a bit
  205. by working in RAD: or some other variety of supposedly recoverable ram:
  206. device. In particular, be wary of any program which opens and sends something
  207. to a hard disk file. It's annoying to have to reconstruct a few megabytes of
  208. hard disk files.
  209.  
  210. Beware the incredibly slow RawDoFmt!!!
  211.  
  212. Back in the early days somewhere around 1986 when the Amiga 1000 was just
  213. called the Amiga, I wrote a disassembler, in BASIC of all things. Later I
  214. translated it into C, using Matt Dillon's DICE, then into Pascal, using Pat
  215. Quaid's PCQ Pascal, and finally into E. A testfile of about 20K was processed
  216. in about 19 seconds with DICE, in about 15 seconds with PCQ, and finally in
  217. about 44 seconds with E. Not good!! After quite a bit of detective work the
  218. problem was traced to the above mentioned RawDoFmt, which hangs out in the
  219. exec library. E uses it in WriteF and StringF, PCQ and DICE don't! I had put
  220. together a stringf function for PCQ, using mostly my code with a bit of help
  221. from an itoa (integer to string) function in the public domain somewhere. I
  222. later passed it on to Joe Siebenmann for his EZAsm. Most recently I rewrote
  223. it as a module for E. The moral of the story is that now my disassembler
  224. processes the same test file in 11 seconds, a clear winner over PCQ which uses
  225. essentially the same stringf, and DICE, which doesn't. The speed increase is
  226. attributable entirely to the stringf function, which is at least 15 times as
  227. fast as StringF. Incidentally, I later translated the disassembler into EZAsm,
  228. and the same file ran in 3 seconds, but that's a different story.
  229.  
  230. If you have need for a fast stringf function, here they are, all 4 of them.
  231. Four because there are two versions, and both come in two options. One option
  232. is for the 68000 and one for the 68020 or above. My Amiga 1200 has the 68020
  233. chip so I figured that I might as well take advantage of the DIVU.L inst.
  234. It doesn't have any noticeable affect on speed, but makes the module about
  235. 80 bytes smaller, for whatever that's worth. Now for the two versions:
  236. qstringf.m and of course qstringf20.m run about twice as fast as stringf.m
  237. and stringf20.m, but don't do as much. The q versions handle format strings
  238. including \n, \c, \d, \h, \s. The versions without q also handle \l, \r,
  239. \z, [n] and (m,n). In other words, for the loss of speed you get left and
  240. right justification, padding with leading zeros, field specifications and
  241. max and min lengths for strings. For most purposes, the quick version will
  242. get the job done faster with smaller code. Don't try to include more than
  243. one version as a module in a program, since both modules will have similarly
  244. named functions. In other words, all four have a procedure called stringf
  245. in them, but each is different. As with the real StringF, the output string
  246. will be an Estring and the return values are the same as for StringF.
  247. How to use stringf:
  248.   Just like StringF EXCEPT, the data stream must be a list!! Example:
  249.       StringF(str,format,datastream) becomes stringf(str,format,[datastream])
  250.       StringF(str,'\d \h \s\n',3,5,'abc') becomes
  251.       stringf(str,'\d \h \s\n',[3,5,'abc'])
  252. If you forget the square brackets , it will give you an error message if you
  253. put more than one argument in the data stream. With just one argument in the
  254. data stream, stringf will assume that argument is a pointer to a list, with
  255. unpredictable results (potential guru?).
  256.  
  257. Incidentally, all versions have an added capability, printout of binary
  258. representations, called by putting %lb in the format string. If you get
  259. your kicks by translating C programs into E, you may not be aware that you
  260. can use the format strings unchanged in most cases. For instance, EC would
  261. translate \h into %lx before passing it to RawDoFmt, so why not just leave it
  262. %lx in the first place?  To see what EC does to various \ options, use the
  263. technique of creating an executable file and disassembling. Don't bother with
  264. the NOP business because the strings will be somewhere else, probably near
  265. the end of the file.
  266.  
  267. StringF(s,'xxx\h[2]xxx\d[2]xxx\n',456,456)
  268. WriteF('string is \s',s)
  269.  
  270. According to my C manual, if the field specification doesn't provide enough
  271. space to print an entire number, the whole number will be printed anyway.
  272. RawDoFmt won't do that. My stringf will. Try the above, which should print
  273. three digits for each number but only prints two.
  274.  
  275. The stringf20.e file is an example of an E file which has been almost fully
  276. translated from E to assembly language. Stringf20.doc is really an early
  277. version of stringf20.e, when the translation process was just getting started.
  278. By comparing the two files, you can find examples of many E structures, such
  279. as REPEAT:UNTIL loops, FOR loops, SELECT:CASE, and various  IF statements.
  280.  
  281.  
  282.  
  283.